﻿#include <QMenu>
#include <QMenuBar>
#include <QMutex>
#include <Configuration.h>
#include <Resources.h>
#include <Constants.h>
#include "ActionManager.h"

namespace Domy {

ActionManager::ActionManager(QWidget *parent)
    : QObject(parent),
      m_menu_bar(new QMenuBar(parent)),
      m_menu_file(Q_NULLPTR),
      m_menu_edit(Q_NULLPTR),
      m_menu_view(Q_NULLPTR),
      m_menu_tools(Q_NULLPTR),
      m_menu_help(Q_NULLPTR)
{
    init();
}

ActionManager *ActionManager::getInstance()
{
    static ActionManager *s_instance = Q_NULLPTR;
    static QMutex s_mutex;
    QMutexLocker lock(&s_mutex);
    if (!s_instance) {
        s_instance = new ActionManager();
    }
    return s_instance;
}

ActionManager::~ActionManager()
{
    delete m_menu_bar;
    m_menu_bar = Q_NULLPTR;
}

QAction *ActionManager::registAction(ActionKey key, const QString &text, const QIcon &icon, const QKeySequence &shorcut)
{
    QAction *action = Q_NULLPTR;

    action = new QAction(icon, text, this);
    action->setData(static_cast<int>(key));
    action->setShortcut(shorcut);
    action->setShortcutContext(Qt::WindowShortcut);

    connect(action, SIGNAL(triggered(bool)), this, SLOT(slotOnActionTriggered(bool)));

    m_action_map.insert(key, action);

    return action;
}

void ActionManager::slotOnActionTriggered(bool is_checked)
{
    QAction *action = dynamic_cast<QAction *>(sender());
    if (!action) {
        return;
    }
    ActionKey key = static_cast<ActionKey>(action->data().toInt());

    emit signalActionTriggered(key, is_checked);
}

void ActionManager::slotOnRecentFileActionTriggered(bool is_checked)
{
    QAction *action = dynamic_cast<QAction *>(sender());
    if (!action) {
        return;
    }
    QString file_path = action->text();
    emit signalRecentFileActionTriggered(file_path);
}

void ActionManager::init()
{
    QAction *action_new_file = registAction(ActKey_NewFile, tr("New File ..."), GET_ICON("new_file"), Qt::CTRL + Qt::Key_N);
    QAction *action_open_file = registAction(ActKey_OpenFile, tr("Open File ..."), GET_ICON("open_file"), Qt::CTRL + Qt::Key_O);
    QAction *action_close_file = registAction(ActKey_CloseFile, tr("Close File"), GET_ICON("close_file"), Qt::CTRL + Qt::Key_W);
    QAction *action_close_all_files = registAction(ActKey_CloseAllFiles, tr("Close All Files ..."), GET_ICON("close_all"), Qt::CTRL + Qt::SHIFT + Qt::Key_W);
    QAction *action_recent = registAction(ActKey_RecentFiles, tr("Recent Files ..."), GET_ICON("recent_files"));
    registAction(ActKey_RecentFilesClear, tr("Clear"), GET_ICON("recent_clear"));
    QAction *action_save = registAction(ActKey_SaveOne, tr("Save"), GET_ICON("save"), Qt::CTRL + Qt::Key_S);
    QAction *action_save_as = registAction(ActKey_SaveAs, tr("Save As ..."), GET_ICON("save_as"));
    QAction *action_save_all = registAction(ActKey_SaveAll, tr("Save All"), GET_ICON("save_all"), Qt::CTRL + Qt::SHIFT + Qt::Key_S);
    QAction *action_exit = registAction(ActKey_Exit, tr("Exit"), GET_ICON("exit"), Qt::CTRL + Qt::Key_Q);
    QAction *action_undo = registAction(ActKey_Undo, tr("Undo"), GET_ICON("undo"), Qt::CTRL + Qt::Key_Z);
    QAction *action_redo = registAction(ActKey_Redo, tr("Redo"), GET_ICON("redo"), Qt::CTRL + Qt::Key_Y);
    QAction *action_cut = registAction(ActKey_Cut, tr("Cut"), GET_ICON("cut"), Qt::CTRL + Qt::Key_X);
    QAction *action_copy = registAction(ActKey_Copy, tr("Copy"), GET_ICON("copy"), Qt::CTRL + Qt::Key_C);
    QAction *action_paste = registAction(ActKey_Paste, tr("Paste"), GET_ICON("paste"), Qt::CTRL + Qt::Key_V);
    QAction *action_select_all = registAction(ActKey_SelectAll, tr("Select All"), GET_ICON("select_all"), Qt::CTRL + Qt::Key_A);
    QAction *action_find = registAction(ActKey_Find, tr("Find ..."), GET_ICON("find"), Qt::CTRL + Qt::Key_F);
    QAction *action_find_prev = registAction(ActKey_FindPrev, tr("Find Previous"), GET_ICON("prev"), Qt::CTRL + Qt::Key_F3);
    QAction *action_find_next = registAction(ActKey_FindNext, tr("Find Next"), GET_ICON("next"), Qt::Key_F3);
    QAction *action_shortcut = registAction(ActKey_Shortcut, tr("Shortcut ..."), GET_ICON("shortcut"));
    QAction *action_shortcut_comment = registAction(ActKey_ShortcutComment, tr("Shortcut Comment"), GET_ICON("shortcut_comment"), Qt::CTRL + Qt::Key_Slash);
    QAction *action_shortcut_apostrophe = registAction(ActKey_ShortcutApostrophe, tr("Shortcut Apostrophe"), GET_ICON("shortcut_apostrophe"), Qt::CTRL + Qt::Key_Apostrophe);
    QAction *action_shortcut_quotation = registAction(ActKey_ShortcutQuotation, tr("Shortcut Quotation"), GET_ICON("shortcut_quotation"), Qt::CTRL + Qt::Key_QuoteDbl);
    QAction *action_shortcut_node = registAction(ActKey_ShortcutAngleBracket, tr("Shortcut Angle Bracket"), GET_ICON("shortcut_angle_bracket"), Qt::CTRL + Qt::Key_Less);
    QAction *action_shortcut_paren = registAction(ActKey_ShortcutParen, tr("Shortcut Paren"), GET_ICON("shortcut_paren"), Qt::CTRL + Qt::Key_ParenLeft);
    QAction *action_shortcut_bracket = registAction(ActKey_ShortcutBracket, tr("Shortcut Bracket"), GET_ICON("shortcut_bracket"), Qt::CTRL + Qt::Key_BracketLeft);
    QAction *action_shortcut_brace = registAction(ActKey_ShortcutBrace, tr("Shortcut Brace"), GET_ICON("shortcut_brace"), Qt::CTRL + Qt::Key_BraceLeft);
    QAction *action_windows_layout = registAction(ActKey_WindowsLayout, tr("Windows Layout"), GET_ICON("layout"));
    QAction *action_layout_load = registAction(ActKey_WindowsLayoutLoad, tr("Load ..."), GET_ICON("layout_load"));
    QAction *action_layout_reset = registAction(ActKey_WindowsLayoutReset, tr("Reset"), GET_ICON("layout_reset"));
    QAction *action_layout_save = registAction(ActKey_WindowsLayoutSave, tr("Save"), GET_ICON("layout_save"));
    QAction *action_layout_save_as = registAction(ActKey_WindowsLayoutSaveAs, tr("Save As ..."), GET_ICON("layout_save_as"));
    QAction *action_settings = registAction(ActKey_Settings, tr("Settings"), GET_ICON("settings"), Qt::CTRL + Qt::SHIFT + Qt::Key_S);
    QAction *action_help = registAction(ActKey_HelpDocument, tr("Help Document"), GET_ICON("help_document"), Qt::CTRL + Qt::Key_F1);
    QAction *action_regist = registAction(ActKey_Regist, tr("Regist Product ..."), GET_ICON("regist"));
    QAction *action_about = registAction(ActKey_AboutUs, tr("About Us"), GET_ICON("about_us"));

    m_menu_file = m_menu_bar->addMenu(tr("File(&F)"));
    m_menu_file->addAction(action_new_file);
    m_menu_file->addAction(action_open_file);
    m_menu_file->addAction(action_recent);
    m_menu_file->addSeparator();
    m_menu_file->addAction(action_close_file);
    m_menu_file->addAction(action_close_all_files);
    updateRecentFiles(); // 更新最近打开文件记录
    m_menu_file->addSeparator();
    m_menu_file->addAction(action_save);
    m_menu_file->addAction(action_save_as);
    m_menu_file->addAction(action_save_all);
    m_menu_file->addSeparator();
    m_menu_file->addAction(action_exit);

    m_menu_edit = m_menu_bar->addMenu(tr("Edit(&E"));
    m_menu_edit->addAction(action_undo);
    m_menu_edit->addAction(action_redo);
    m_menu_edit->addSeparator();
    m_menu_edit->addAction(action_cut);
    m_menu_edit->addAction(action_copy);
    m_menu_edit->addAction(action_paste);
    m_menu_edit->addSeparator();
    m_menu_edit->addAction(action_select_all);
    m_menu_edit->addSeparator();
    m_menu_edit->addAction(action_find);
    m_menu_edit->addAction(action_find_prev);
    m_menu_edit->addAction(action_find_next);
    m_menu_edit->addSeparator();
    m_menu_edit->addAction(action_shortcut);
    {
        QMenu *menu_shortcut = new QMenu(m_menu_bar);
        menu_shortcut->addAction(action_shortcut_node);
        menu_shortcut->addAction(action_shortcut_comment);
        menu_shortcut->addAction(action_shortcut_apostrophe);
        menu_shortcut->addAction(action_shortcut_quotation);
        menu_shortcut->addAction(action_shortcut_paren);
        menu_shortcut->addAction(action_shortcut_bracket);
        menu_shortcut->addAction(action_shortcut_brace);
        action_shortcut->setMenu(menu_shortcut);
    }

    m_menu_view = m_menu_bar->addMenu(tr("Views(&V"));
    {
        QMenu *menu_layout = new QMenu(m_menu_bar);
        menu_layout->addAction(action_layout_load);
        menu_layout->addAction(action_layout_reset);
        menu_layout->addAction(action_layout_save);
        menu_layout->addAction(action_layout_save_as);
        action_windows_layout->setMenu(menu_layout);
    }
    updateViewMenu();

    m_menu_tools = m_menu_bar->addMenu(tr("Tools(&T)"));
    m_menu_tools->addSeparator();
    m_menu_tools->addAction(action_settings);

    m_menu_help = m_menu_bar->addMenu(tr("Help(&H)"));
    m_menu_help->addAction(action_help);
    m_menu_help->addSeparator();
    m_menu_help->addAction(action_regist);
    m_menu_help->addAction(action_about);
}

QAction *ActionManager::registRecentFileAction(const QString &text)
{
    QAction *action = new QAction(text, this);
    action->setData(static_cast<int>(ActKey_RecentFilePath));

    connect(action, SIGNAL(triggered(bool)), this, SLOT(slotOnRecentFileActionTriggered(bool)));

    m_action_map.insert(ActKey_RecentFilePath, action);

    return action;
}

QAction *ActionManager::getAction(ActionKey key)
{
    QAction *action = Q_NULLPTR;
    if (m_action_map.contains(key)) {
        action = m_action_map.value(key);
    }
    return action;
}

void ActionManager::setActionEnable(ActionKey key, bool enable)
{
    for (auto it = m_action_map.find(key); it != m_action_map.end(); ++it) {
        if (it.key() == key) {
            if (*it) {
                it.value()->setEnabled(enable);
            }
        }
        else {
            break;
        }
    }
}

void ActionManager::removeAction(ActionKey key)
{
    for (auto it = m_action_map.find(key); it != m_action_map.end(); ++it) {
        if (it.key() == key) {
            delete *it;
            *it = Q_NULLPTR;
        }
        else {
            break;
        }
    }
    m_action_map.remove(key);
}

void ActionManager::triggerAction(ActionKey key)
{
    QAction *action = getAction(key);
    if (action) {
        action->trigger();
    }
}

QMenuBar *ActionManager::getMenuBar()
{
    return m_menu_bar;
}

void ActionManager::updateRecentFiles()
{
    QAction *recent_action = getAction(ActKey_RecentFiles);
    QMenu *menu = recent_action->menu();
    if (!menu) {
        menu = new QMenu(m_menu_bar);
        recent_action->setMenu(menu);
    }
    menu->clear();
    m_action_map.remove(ActKey_RecentFilePath);
    QStringList recent_files = domy_config->getRecentFilesList();
    for (const QString &file_path : recent_files) {
        QAction *action = registRecentFileAction(file_path);
        menu->addAction(action);
    }
    menu->addSeparator();
    menu->addAction(getAction(ActKey_RecentFilesClear));
    recent_action->setEnabled(!recent_files.isEmpty());
}

void ActionManager::addToolBarAction(QAction *action, bool update)
{
    m_tool_bar_actions.append(action);
    if (update) {
        updateViewMenu();
    }
}

void ActionManager::addDockWindowAction(QAction *action, bool update)
{
    m_dock_window_actions.append(action);
    if (update) {
        updateViewMenu();
    }
}

void ActionManager::updateViewMenu()
{
    m_menu_view->clear();

    for (QAction *action : m_tool_bar_actions) {
        m_menu_view->addAction(action);
    }

    m_menu_view->addSeparator();

    for (QAction *action : m_dock_window_actions) {
        m_menu_view->addAction(action);
    }

    m_menu_view->addSeparator();

    m_menu_view->addAction(getAction(ActKey_WindowsLayout));
}

} // namespace Domy
